/*-------------------------------------------------------------------------
 *
 * arraysubs.c
 *	  数组的下标支持函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/arraysubs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "executor/execExpr.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/subscripting.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"


/* SubscriptingRefState.workspace 用于数组下标执行 */
typedef struct ArraySubWorkspace
{
	/* 在表达式编译过程中确定的值 */
	Oid			refelemtype;	/* 数组元素类型的 OID */
	int16		refattrlength;	/* 数组类型的 typlen */
	int16		refelemlength;	/* 数组元素类型的 typlen */
	bool		refelembyval;	/* 元素类型是值传递吗？ */
	char		refelemalign;	/* 元素类型的 typalign */

	/*
	 * 作为整数转换的下标值。请注意，这些数组的长度必须为
	 * MAXDIM，即使处理的下标更少，因为
	 * array_get/set_slice 可能会在额外条目上涂鸦。
	 */
	int			upperindex[MAXDIM];
	int			lowerindex[MAXDIM];
} ArraySubWorkspace;


/*
 * 完成数组的 SubscriptingRef 表达式的解析分析。
 *
 * 转换下标表达式，将其强制为整数，
 * 并确定 SubscriptingRef 节点的结果类型。
 */
static void fc_array_subscript_transform(SubscriptingRef *fc_sbsref,
						  List *fc_indirection,
						  ParseState *fc_pstate,
						  bool fc_isSlice,
						  bool fc_isAssignment)
{
	List	   *fc_upperIndexpr = NIL;
	List	   *fc_lowerIndexpr = NIL;
	ListCell   *fc_idx;

	/*
	 * 转换下标表达式，并将上界和下界分离为两个列表。
	 *
	 * 如果我们有一个容器切片表达式，我们将任何非切片
	 * 间接项转换为切片，通过将单个下标视为
	 * 上界，并提供假定的下界 1。
	 */
	foreach(fc_idx, fc_indirection)
	{
		A_Indices  *fc_ai = lfirst_node(A_Indices, fc_idx);
		Node	   *fc_subexpr;

		if (fc_isSlice)
		{
			if (fc_ai->lidx)
			{
				fc_subexpr = transformExpr(fc_pstate, fc_ai->lidx, fc_pstate->p_expr_kind);
				/* 如果它还不是 int4，尝试强制转换 */
				fc_subexpr = coerce_to_target_type(fc_pstate,
												fc_subexpr, exprType(fc_subexpr),
												INT4OID, -1,
												COERCION_ASSIGNMENT,
												COERCE_IMPLICIT_CAST,
												-1);
				if (fc_subexpr == NULL)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("array subscript must have type integer"),
							 parser_errposition(fc_pstate, exprLocation(fc_ai->lidx))));
			}
			else if (!fc_ai->is_slice)
			{
				/* 生成常量 1 */
				fc_subexpr = (Node *) makeConst(INT4OID,
											 -1,
											 InvalidOid,
											 sizeof(int32),
											 Int32GetDatum(1),
											 false,
											 true); /* 值传递 */
			}
			else
			{
				/* 省略下界的切片，将 NULL 放入列表中 */
				fc_subexpr = NULL;
			}
			fc_lowerIndexpr = lappend(fc_lowerIndexpr, fc_subexpr);
		}
		else
			Assert(fc_ai->lidx == NULL && !fc_ai->is_slice);

		if (fc_ai->uidx)
		{
			fc_subexpr = transformExpr(fc_pstate, fc_ai->uidx, fc_pstate->p_expr_kind);
			/* 如果它还不是 int4，尝试强制转换 */
			fc_subexpr = coerce_to_target_type(fc_pstate,
											fc_subexpr, exprType(fc_subexpr),
											INT4OID, -1,
											COERCION_ASSIGNMENT,
											COERCE_IMPLICIT_CAST,
											-1);
			if (fc_subexpr == NULL)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("array subscript must have type integer"),
						 parser_errposition(fc_pstate, exprLocation(fc_ai->uidx))));
		}
		else
		{
			/* 省略上界的切片，将 NULL 放入列表中 */
			Assert(fc_isSlice && fc_ai->is_slice);
			fc_subexpr = NULL;
		}
		fc_upperIndexpr = lappend(fc_upperIndexpr, fc_subexpr);
	}

	/* ... 并将转换后的列表存储到 SubscriptRef 节点中 */
	fc_sbsref->refupperindexpr = fc_upperIndexpr;
	fc_sbsref->reflowerindexpr = fc_lowerIndexpr;

	/* 验证下标列表长度在实现限制内 */
	if (list_length(fc_upperIndexpr) > MAXDIM)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
						list_length(fc_upperIndexpr), MAXDIM)));
	/* 我们无需单独检查 lowerIndexpr */

	/*
	 * 确定下标操作的结果类型。如果我们在切片，
	 * 那么它与数组类型相同，否则它是元素类型。在
	 * 这两种情况下，typmod 与数组的相同，所以我们无需
	 * 更改 reftypmod。
	 */
	if (fc_isSlice)
		fc_sbsref->refrestype = fc_sbsref->refcontainertype;
	else
		fc_sbsref->refrestype = fc_sbsref->refelemtype;
}

/*
 * 在执行期间，处理 SubscriptingRef 表达式中的下标。
 *
 * 下标表达式已经在 SubscriptingRefState 的数组中以 Datum 形式求值。
 * 根据需要检查并转换它们。
 *
 * 如果任何下标为 NULL，则在赋值情况下抛出错误，或者在获取
 * 情况下将结果设置为 NULL 并返回 false（指示调用者跳过
 * 剩余的 SubscriptingRef 序列）。
 *
 * 我们将所有下标转换为普通整数，并将它们保存在
 * sbsrefstate->workspace 数组中。
 */
static bool fc_array_subscript_check_subscripts(ExprState *fc_state,
								 ExprEvalStep *fc_op,
								 ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref_subscript.state;
	ArraySubWorkspace *fc_workspace = (ArraySubWorkspace *) fc_sbsrefstate->workspace;

	/* 处理上标 */
	for (int fc_i = 0; fc_i < fc_sbsrefstate->numupper; fc_i++)
	{
		if (fc_sbsrefstate->upperprovided[fc_i])
		{
			/* 如果任何索引表达式返回 NULL，则结果为 NULL 或错误 */
			if (fc_sbsrefstate->upperindexnull[fc_i])
			{
				if (fc_sbsrefstate->isassignment)
					ereport(ERROR,
							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
							 errmsg("array subscript in assignment must not be null")));
				*fc_op->resnull = true;
				return false;
			}
			fc_workspace->upperindex[fc_i] = DatumGetInt32(fc_sbsrefstate->upperindex[fc_i]);
		}
	}

	/* 同样适用于下标 */
	for (int fc_i = 0; fc_i < fc_sbsrefstate->numlower; fc_i++)
	{
		if (fc_sbsrefstate->lowerprovided[fc_i])
		{
			/* 如果任何索引表达式返回 NULL，则结果为 NULL 或错误 */
			if (fc_sbsrefstate->lowerindexnull[fc_i])
			{
				if (fc_sbsrefstate->isassignment)
					ereport(ERROR,
							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
							 errmsg("array subscript in assignment must not be null")));
				*fc_op->resnull = true;
				return false;
			}
			fc_workspace->lowerindex[fc_i] = DatumGetInt32(fc_sbsrefstate->lowerindex[fc_i]);
		}
	}

	return true;
}

/*
 * 评估数组元素的 SubscriptingRef 获取。
 *
 * 源容器在步骤的结果变量中（它被知道不是 NULL，因为
 * 我们将 fetch_strict 设置为 true），并且索引已被评估到
 * 工作区数组中。
 */
static void fc_array_subscript_fetch(ExprState *fc_state,
					  ExprEvalStep *fc_op,
					  ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	ArraySubWorkspace *fc_workspace = (ArraySubWorkspace *) fc_sbsrefstate->workspace;

	/* 如果源数组（或任何下标）为 NULL，不应到达这里 */
	Assert(!(*fc_op->resnull));

	*fc_op->resvalue = array_get_element(*fc_op->resvalue,
									  fc_sbsrefstate->numupper,
									  fc_workspace->upperindex,
									  fc_workspace->refattrlength,
									  fc_workspace->refelemlength,
									  fc_workspace->refelembyval,
									  fc_workspace->refelemalign,
									  fc_op->resnull);
}

/*
 * 评估数组切片的 SubscriptingRef 获取。
 *
 * 源容器在步骤的结果变量中（它被知道不是 NULL，因为
 * 我们将 fetch_strict 设置为 true），并且索引已被评估到
 * 工作区数组中。
 */
static void fc_array_subscript_fetch_slice(ExprState *fc_state,
							ExprEvalStep *fc_op,
							ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	ArraySubWorkspace *fc_workspace = (ArraySubWorkspace *) fc_sbsrefstate->workspace;

	/* 如果源数组（或任何下标）为 NULL，不应到达这里 */
	Assert(!(*fc_op->resnull));

	*fc_op->resvalue = array_get_slice(*fc_op->resvalue,
									fc_sbsrefstate->numupper,
									fc_workspace->upperindex,
									fc_workspace->lowerindex,
									fc_sbsrefstate->upperprovided,
									fc_sbsrefstate->lowerprovided,
									fc_workspace->refattrlength,
									fc_workspace->refelemlength,
									fc_workspace->refelembyval,
									fc_workspace->refelemalign);
	/* 切片永远不会是 NULL，因此不需要更改 *op->resnull */
}

/*
 * 评估数组元素赋值的 SubscriptingRef 赋值。
 *
 * 输入容器（可能为 NULL）在结果区域中，替换值在
 * SubscriptingRefState 的 replacevalue/replacenull 中。
 */
static void fc_array_subscript_assign(ExprState *fc_state,
					   ExprEvalStep *fc_op,
					   ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	ArraySubWorkspace *fc_workspace = (ArraySubWorkspace *) fc_sbsrefstate->workspace;
	Datum		fc_arraySource = *fc_op->resvalue;

	/*
	 * 对于固定长度数组类型的赋值，原始数组和要赋值的值
	 * 必须都非 NULL，否则我们返回原始数组。
	 */
	if (fc_workspace->refattrlength > 0)
	{
		if (*fc_op->resnull || fc_sbsrefstate->replacenull)
			return;
	}

	/*
	 * 对于 varlena 数组的赋值，我们通过替换为空（零维）数组来处理 NULL 原始数组；
	 * 新元素的插入将导致一个单一元素数组值。 不管新元素是否为 NULL，
	 * 都没有关系。
	 */
	if (*fc_op->resnull)
	{
		fc_arraySource = PointerGetDatum(construct_empty_array(fc_workspace->refelemtype));
		*fc_op->resnull = false;
	}

	*fc_op->resvalue = array_set_element(fc_arraySource,
									  fc_sbsrefstate->numupper,
									  fc_workspace->upperindex,
									  fc_sbsrefstate->replacevalue,
									  fc_sbsrefstate->replacenull,
									  fc_workspace->refattrlength,
									  fc_workspace->refelemlength,
									  fc_workspace->refelembyval,
									  fc_workspace->refelemalign);
	/* 结果永远不会是 NULL，因此不需要更改 *op->resnull */
}

/*
 * 评估数组切片赋值的 SubscriptingRef 赋值。
 *
 * 输入容器（可能为 NULL）在结果区域中，替换值在
 * SubscriptingRefState 的 replacevalue/replacenull 中。
 */
static void fc_array_subscript_assign_slice(ExprState *fc_state,
							 ExprEvalStep *fc_op,
							 ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	ArraySubWorkspace *fc_workspace = (ArraySubWorkspace *) fc_sbsrefstate->workspace;
	Datum		fc_arraySource = *fc_op->resvalue;

	/*
	 * 对于固定长度数组类型的赋值，原始数组和要赋值的值
	 * 必须都非 NULL，否则我们返回原始数组。
	 */
	if (fc_workspace->refattrlength > 0)
	{
		if (*fc_op->resnull || fc_sbsrefstate->replacenull)
			return;
	}

	/*
	 * 对于 varlena 数组的赋值，我们通过替换为空（零维）数组来处理 NULL 原始数组；
	 * 新元素的插入将导致一个单一元素数组值。 不管新元素是否为 NULL，
	 * 都没有关系。
	 */
	if (*fc_op->resnull)
	{
		fc_arraySource = PointerGetDatum(construct_empty_array(fc_workspace->refelemtype));
		*fc_op->resnull = false;
	}

	*fc_op->resvalue = array_set_slice(fc_arraySource,
									fc_sbsrefstate->numupper,
									fc_workspace->upperindex,
									fc_workspace->lowerindex,
									fc_sbsrefstate->upperprovided,
									fc_sbsrefstate->lowerprovided,
									fc_sbsrefstate->replacevalue,
									fc_sbsrefstate->replacenull,
									fc_workspace->refattrlength,
									fc_workspace->refelemlength,
									fc_workspace->refelembyval,
									fc_workspace->refelemalign);
	/* 结果永远不会是 NULL，因此不需要更改 *op->resnull */
}

/*
 * 计算 SubscriptingRef 赋值表达式的旧数组元素值。
 * 仅在新值子表达式包含 SubscriptingRef 或 FieldStore 时调用。
 * 这与常规获取情况相同，只是我们必须处理 NULL 数组，
 * 并且值应存储到 SubscriptingRefState 的
 * prevvalue/prevnull 字段中。
 */
static void fc_array_subscript_fetch_old(ExprState *fc_state,
						  ExprEvalStep *fc_op,
						  ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	ArraySubWorkspace *fc_workspace = (ArraySubWorkspace *) fc_sbsrefstate->workspace;

	if (*fc_op->resnull)
	{
		/* 整个数组为 NULL，因此任何元素也是 */
		fc_sbsrefstate->prevvalue = (Datum) 0;
		fc_sbsrefstate->prevnull = true;
	}
	else
		fc_sbsrefstate->prevvalue = array_get_element(*fc_op->resvalue,
												   fc_sbsrefstate->numupper,
												   fc_workspace->upperindex,
												   fc_workspace->refattrlength,
												   fc_workspace->refelemlength,
												   fc_workspace->refelembyval,
												   fc_workspace->refelemalign,
												   &fc_sbsrefstate->prevnull);
}

/*
 * 计算 SubscriptingRef 赋值表达式的旧数组切片值。
 * 仅在新值子表达式包含 SubscriptingRef 或 FieldStore 时调用。
 * 这与常规获取情况相同，只是我们必须处理 NULL 数组，
 * 并且值应存储到 SubscriptingRefState 的
 * prevvalue/prevnull 字段中。
 *
 * 注意：目前这是死代码，因为切片的新值必须是一个数组，
 * 因此它不能直接包含一个 FieldStore；也不能包含一个 SubscriptingRef 赋值，因为
 * 我们认为相邻的下标用于索引一个多维数组，而不是嵌套数组类型。
 * 未来的泛化可能使这一点可达，但目前。
 */
static void fc_array_subscript_fetch_old_slice(ExprState *fc_state,
								ExprEvalStep *fc_op,
								ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	ArraySubWorkspace *fc_workspace = (ArraySubWorkspace *) fc_sbsrefstate->workspace;

	if (*fc_op->resnull)
	{
		/* 整个数组为 NULL，因此任何切片也是 */
		fc_sbsrefstate->prevvalue = (Datum) 0;
		fc_sbsrefstate->prevnull = true;
	}
	else
	{
		fc_sbsrefstate->prevvalue = array_get_slice(*fc_op->resvalue,
												 fc_sbsrefstate->numupper,
												 fc_workspace->upperindex,
												 fc_workspace->lowerindex,
												 fc_sbsrefstate->upperprovided,
												 fc_sbsrefstate->lowerprovided,
												 fc_workspace->refattrlength,
												 fc_workspace->refelemlength,
												 fc_workspace->refelembyval,
												 fc_workspace->refelemalign);
		/* 非 NULL 数组的切片永远不会为 NULL */
		fc_sbsrefstate->prevnull = false;
	}
}

/*
 * 设置数组下标操作的执行状态。
 */
static void fc_array_exec_setup(const SubscriptingRef *fc_sbsref,
				 SubscriptingRefState *fc_sbsrefstate,
				 SubscriptExecSteps *fc_methods)
{
	bool		fc_is_slice = (fc_sbsrefstate->numlower != 0);
	ArraySubWorkspace *fc_workspace;

	/*
	 * 强制实施数组下标数量的实现限制。这个检查与解析时的检查并不完全冗余；
	 * 理论上表达式可能由另一个具有不同 MAXDIM 值的后端存储。
	 */
	if (fc_sbsrefstate->numupper > MAXDIM)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
						fc_sbsrefstate->numupper, MAXDIM)));

	/* 如果解析器正常，则这应该不可能，但仍然检查： */
	if (fc_sbsrefstate->numlower != 0 &&
		fc_sbsrefstate->numupper != fc_sbsrefstate->numlower)
		elog(ERROR, "upper and lower index lists are not same length");

	/*
	 * 分配特定类型的工作区。
	 */
	fc_workspace = (ArraySubWorkspace *) palloc(sizeof(ArraySubWorkspace));
	fc_sbsrefstate->workspace = fc_workspace;

	/*
	 * 收集执行时所需的数据类型细节。
	 */
	fc_workspace->refelemtype = fc_sbsref->refelemtype;
	fc_workspace->refattrlength = get_typlen(fc_sbsref->refcontainertype);
	get_typlenbyvalalign(fc_sbsref->refelemtype,
						 &fc_workspace->refelemlength,
						 &fc_workspace->refelembyval,
						 &fc_workspace->refelemalign);

	/*
	 * 返回指向适当步骤执行函数的指针。
	 */
	fc_methods->sbs_check_subscripts = fc_array_subscript_check_subscripts;
	if (fc_is_slice)
	{
		fc_methods->sbs_fetch = fc_array_subscript_fetch_slice;
		fc_methods->sbs_assign = fc_array_subscript_assign_slice;
		fc_methods->sbs_fetch_old = fc_array_subscript_fetch_old_slice;
	}
	else
	{
		fc_methods->sbs_fetch = fc_array_subscript_fetch;
		fc_methods->sbs_assign = fc_array_subscript_assign;
		fc_methods->sbs_fetch_old = fc_array_subscript_fetch_old;
	}
}

/*
 * array_subscript_handler
 *		标准 varlena 数组的下标处理器。
 *
 * 这应该仅用于“真正的”数组类型，它们具有由 varlena 数组例程
 * 理解的数组头，并由元素类型的 pg_type.typarray 字段引用。
 */
Datum array_subscript_handler(PG_FUNCTION_ARGS)
{
	static const SubscriptRoutines fc_sbsroutines = {
		.transform = fc_array_subscript_transform,
		.exec_setup = fc_array_exec_setup,
		.fetch_strict = true,	/* 对于NULL输入，fetch返回NULL */
		.fetch_leakproof = true,	/* 对于错误的下标，fetch返回NULL */
		.store_leakproof = false	/* ...但赋值抛出错误 */
	};

	PG_RETURN_POINTER(&fc_sbsroutines);
}

/*
 * raw_array_subscript_handler
 *		“原生”数组的下标处理器。
 *
 * “原生”数组仅包含 N 个独立的元素类型实例。
 * 目前我们要求元素类型和数组类型都是固定长度的，但对于数组类型放宽这个限制并不困难。
 *
 * 目前，所有支持代码与标准 varlena 数组共享。
 * 我们可能将它们分成不同的代码路径，但这可能只会产生边际的速度提升。
 * 拥有单独处理程序的主要目的在于使 pg_type.typsubscript 清楚地指示类型的语义。
 */
Datum raw_array_subscript_handler(PG_FUNCTION_ARGS)
{
	static const SubscriptRoutines fc_sbsroutines = {
		.transform = fc_array_subscript_transform,
		.exec_setup = fc_array_exec_setup,
		.fetch_strict = true,	/* 对于NULL输入，fetch返回NULL */
		.fetch_leakproof = true,	/* 对于错误的下标，fetch返回NULL */
		.store_leakproof = false	/* ...但赋值抛出错误 */
	};

	PG_RETURN_POINTER(&fc_sbsroutines);
}
